Skip to contentMethod: cloneObject(Object, Field, Object, CloneConfiguration)
1: /**
2: * Copyright (C) 2020 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.sessions;
16:
17: import cz.cvut.kbss.jopa.adapters.IndirectCollection;
18: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
19: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
20:
21: import java.lang.reflect.Constructor;
22: import java.lang.reflect.Field;
23: import java.lang.reflect.InvocationTargetException;
24: import java.security.AccessController;
25: import java.security.PrivilegedActionException;
26: import java.util.Collection;
27: import java.util.Collections;
28: import java.util.HashMap;
29: import java.util.Map;
30: import java.util.Map.Entry;
31:
32: class MapInstanceBuilder extends AbstractInstanceBuilder {
33:
34: private static final Class<?> singletonMapClass = Collections.singletonMap(null, null)
35: .getClass();
36:
37: MapInstanceBuilder(CloneBuilderImpl builder, UnitOfWork uow) {
38: super(builder, uow);
39: this.populates = true;
40: }
41:
42: @Override
43: Object buildClone(Object cloneOwner, Field field, Object original, CloneConfiguration configuration) {
44: Map<?, ?> orig = (Map<?, ?>) original;
45: if (original instanceof IndirectCollection) {
46: orig = ((IndirectCollection<Map<?, ?>>) original).getReferencedCollection();
47: }
48: final Class<?> origCls = orig.getClass();
49: Map<?, ?> clone;
50: clone = cloneUsingDefaultConstructor(cloneOwner, field, origCls, orig, configuration);
51: if (clone == null) {
52: if (singletonMapClass.isInstance(orig)) {
53: clone = buildSingletonClone(cloneOwner, field, orig, configuration);
54: } else {
55: throw new IllegalArgumentException("Unsupported map type " + origCls);
56: }
57: }
58: clone = (Map<?, ?>) builder.createIndirectCollection(clone, cloneOwner, field);
59: return clone;
60:
61: }
62:
63: private Map<?, ?> cloneUsingDefaultConstructor(Object cloneOwner, Field field, Class<?> origCls, Map<?, ?> original,
64: CloneConfiguration configuration) {
65: Map<?, ?> result = createNewInstance(origCls, original.size());
66: if (result != null) {
67: cloneMapContent(cloneOwner, field, original, result, configuration);
68: }
69: return result;
70: }
71:
72: private static Map<?, ?> createNewInstance(Class<?> type, int size) {
73: Map<?, ?> result = null;
74: final Class<?>[] types = {int.class};
75: Object[] params;
76: Constructor<?> c = getDeclaredConstructorFor(type, types);
77: if (c != null) {
78: params = new Object[1];
79: params[0] = size;
80: } else {
81: c = getDeclaredConstructorFor(type, null);
82: params = null;
83: }
84: if (c == null) {
85: return null;
86: }
87: try {
88: result = (Map<?, ?>) c.newInstance(params);
89: } catch (InstantiationException | IllegalArgumentException | InvocationTargetException e) {
90: throw new OWLPersistenceException(e);
91: } catch (IllegalAccessException e) {
92: logConstructorAccessException(c, e);
93: try {
94: result = (Map<?, ?>) AccessController
95: .doPrivileged(new PrivilegedInstanceCreator(c));
96: } catch (PrivilegedActionException ex) {
97: logPrivilegedConstructorAccessException(c, ex);
98: // Do nothing
99: }
100: }
101: return result;
102: }
103:
104: private Map<?, ?> buildSingletonClone(Object cloneOwner, Field field, Map<?, ?> orig,
105: CloneConfiguration configuration) {
106: Entry<?, ?> e = orig.entrySet().iterator().next();
107: Object key = CloneBuilderImpl.isImmutable(e.getKey()) ? e.getKey() :
108: cloneObject(cloneOwner, field, e.getKey(), configuration);
109: Object value = CloneBuilderImpl.isImmutable(e.getValue()) ? e.getValue() :
110: cloneObject(cloneOwner, field, e.getValue(), configuration);
111: if (value instanceof Collection || value instanceof Map) {
112: value = builder.createIndirectCollection(value, cloneOwner, field);
113: }
114: return Collections.singletonMap(key, value);
115: }
116:
117: private void cloneMapContent(Object cloneOwner, Field field, Map<?, ?> source,
118: Map<?, ?> target, CloneConfiguration configuration) {
119: if (source.isEmpty()) {
120: return;
121: }
122: final Map<Object, Object> m = (Map<Object, Object>) target;
123: final Entry<?, ?> tmp = source.entrySet().iterator().next();
124: // Note: If we encounter null -> null mapping first, the whole map will be treated as immutable type map, which can be incorrect
125: final boolean keyPrimitive = CloneBuilderImpl.isImmutable(tmp.getKey());
126: final boolean valuePrimitive = CloneBuilderImpl.isImmutable(tmp.getValue());
127: for (Entry<?, ?> e : source.entrySet()) {
128: Object key;
129: Object value;
130: if (keyPrimitive) {
131: if (valuePrimitive) {
132: m.putAll(source);
133: break;
134: }
135: key = e.getKey();
136: value = cloneObject(cloneOwner, field, e.getValue(), configuration);
137: } else {
138: key = cloneObject(cloneOwner, field, e.getKey(), configuration);
139: value = valuePrimitive ? e.getValue() : cloneObject(cloneOwner, field, e.getValue(), configuration);
140: }
141: m.put(key, value);
142: }
143: }
144:
145: private Object cloneObject(Object owner, Field field, Object obj, CloneConfiguration configuration) {
146: Object clone;
147:• if (obj == null) {
148: clone = null;
149:• } else if (builder.isTypeManaged(obj.getClass())) {
150: clone = uow.registerExistingObject(obj, configuration.getDescriptor(), configuration.getPostRegister());
151: } else {
152: clone = builder.buildClone(owner, field, obj, configuration.getDescriptor());
153: }
154: return clone;
155: }
156:
157: @Override
158: void mergeChanges(Field field, Object target, Object originalValue, Object cloneValue) {
159: assert (originalValue == null) || (originalValue instanceof Map);
160: assert cloneValue instanceof Map;
161:
162: Map<Object, Object> orig = (Map<Object, Object>) originalValue;
163: Map<Object, Object> clone = (Map<Object, Object>) cloneValue;
164: if (clone instanceof IndirectCollection) {
165: clone = ((IndirectCollection<Map<Object, Object>>) clone).getReferencedCollection();
166: }
167: if (orig == null) {
168: orig = (Map<Object, Object>) createNewInstance(clone.getClass(), clone.size());
169: if (orig == null) {
170: orig = createDefaultMap(clone.size());
171: }
172: EntityPropertiesUtils.setFieldValue(field, target, orig);
173: }
174: orig.clear();
175: if (clone.isEmpty()) {
176: return;
177: }
178: for (Entry<?, ?> e : clone.entrySet()) {
179: final Object key = e.getKey();
180: final Object value = e.getValue();
181: final Object keyToPut = uow.contains(key) ? builder.getOriginal(key) : key;
182: final Object valueToPut = uow.contains(value) ? builder.getOriginal(value) : value;
183: orig.put(keyToPut, valueToPut);
184: }
185: }
186:
187: private static Map<Object, Object> createDefaultMap(int size) {
188: return new HashMap<>(size > 1 ? size : 16);
189: }
190: }